#include <stdio.h>
#include "propackfont.h"
#include "mutablebitmapbase.h"
#include "woopsistring.h"
#include "stringiterator.h"

using namespace WoopsiUI;

PropackFont::PropackFont(const char *glyphFileName)
{
	FILE *pf=fopen(glyphFileName,"r");
	if(pf==NULL) return;
	
	fseek(pf,0,SEEK_END);
	u32 filesize = ftell(pf);
	fseek(pf,0,SEEK_SET);
	
	u32 FontCount;
	
	if(filesize<2048){
		FontCount=0x80;
	}else{
		FontCount=0x10000;
	}
	
	_glyphTable=(u16**)malloc(FontCount*4);
	  
	u32 DataSize=filesize-(FontCount*1);
	_glyphData=(u16*)malloc(DataSize);
	  
	u8 *pDiffTbl=(u8*)malloc(FontCount*1);
		  
	fread(pDiffTbl,FontCount,1,pf);
	_spWidth=pDiffTbl[0];
	pDiffTbl[0]=0;
	  
	fread(_glyphData,DataSize,1,pf);
	  
	fclose(pf);
	
	_height=_glyphData[0];
	
	u32 LastOffset=0;
	  
	for(int idx=0;idx<FontCount;idx++){
		u32 diff=pDiffTbl[idx];
		if(diff==0){
			static u16 WidthZero=0;
			_glyphTable[idx]=&WidthZero;
		}else{
			LastOffset+=diff;
			_glyphTable[idx]=&_glyphData[LastOffset/2];
		}
	}

	if(pDiffTbl!=NULL){
		free(pDiffTbl); pDiffTbl=NULL;
	}	  
}

PropackFont::PropackFont(const u8 *glyphData,const int glyphSize) : FontBase()
{
	u32 FontCount;

	if(glyphSize<2048){
		FontCount=0x80;
	}else{
		FontCount=0x10000;
	}

	_glyphTable=(u16**)malloc(FontCount*4);

	u32 DataSize=glyphSize-(FontCount*1);
	_glyphData=(u16*)malloc(DataSize);

	_spWidth=glyphData[0];

	memcpy(_glyphData,&glyphData[FontCount],DataSize);

	_height=_glyphData[0];

	u32 LastOffset=0;

	for(int idx=0;idx<FontCount;idx++){
		u32 diff=glyphData[idx];
		if(diff==0 || idx==0){
			static u16 WidthZero=0;
			_glyphTable[idx]=&WidthZero;
		}else{
			LastOffset+=diff;
			_glyphTable[idx]=&_glyphData[LastOffset/2];
		}
	}
}

u16* PropackFont::GetGlyphData(u32 letter) const
{
	u16 *pGlyphData=_glyphTable[letter];
	if(*pGlyphData!=0) return(pGlyphData);
  
	return(_glyphTable[(u32)'?']);
}

u8 PropackFont::getCharWidth(u32 letter) const {
	// Abort if the font not read
	if(_glyphData == NULL || _glyphTable == NULL) return 0;
	
	u16 *GlyphData=GetGlyphData(letter);
	  
	int Width=*GlyphData;
	  
	if((0x0600<=letter)&&(letter<0x0700)) Width--; // UNICODE Arabic
	if((0xfb50<=letter)&&(letter<0xfe00)) Width--; // UNICODE Arabic Presentation Forms-A
	if((0xfe70<=letter)&&(letter<0xff00)) Width--; // UNICODE Arabic Presentation Forms-B
	  
	return((u8)Width+_spWidth);
}

bool PropackFont::isCharBlank(const u32 letter) const {
	// Abort if the font not read
	if(_glyphData == NULL || _glyphTable == NULL) return true;
	if(*_glyphTable[letter]!=0) return false;
	return true;
}

u16 PropackFont::getStringWidth(const WoopsiString& text) const {
	return getStringWidth(text, 0, text.getLength());
}

u16 PropackFont::getStringWidth(const WoopsiString& text, s32 startIndex, s32 length) const {
	u16 total = 0;

	StringIterator* iterator = text.newStringIterator();
	if (iterator->moveTo(startIndex)) {
	
		do {
			total += getCharWidth(iterator->getCodePoint());
		} while (iterator->moveToNext() && (iterator->getIndex() < startIndex + length));
	}

	delete iterator;

	return total;
}

s16 PropackFont::drawChar(
	MutableBitmapBase* bitmap,
	u32 letter,
	u16 colour,
	s16 x, s16 y,
	u16 clipX1, u16 clipY1, u16 clipX2, u16 clipY2)
{
	// Abort if the font not read
	if(_glyphData == NULL || _glyphTable == NULL) return x;
		
	if (letter == 10 || letter == 13) return x;
	
	const u16 *GlyphData=GetGlyphData(letter);
	  
	int Width=*GlyphData++;
	if(Width==0) return x + _spWidth;
	
	// pass off to a subclass for rendering
	renderChar(GlyphData,Width,bitmap,colour,x, y,clipX1, clipY1, clipX2, clipY2);
	
	return x + getCharWidth(letter);
}

void PropackFont::renderChar(
		const u16 *pixelData, u16 pixelsPerRow,
		MutableBitmapBase* bitmap,
		u16 colour,
		s16 x, s16 y,
		u16 clipX1, u16 clipY1, u16 clipX2, u16 clipY2)
{
	// Abort if the font not read
	if(_glyphData == NULL || _glyphTable == NULL) return;
	
	// Abort if there is nothing to render
	if ((clipY2 < y) ||
			(clipY1 > y + getHeight() - 1) ||
			(x > clipX2) ||
			(x + pixelsPerRow - 1 < clipX1)) return;
		
	u16 rowMax = 16;
	u16 curr;
	u16 mask;
	
	// adjust clipY2 to be the last row in the glyph
	// so we only write while (y=<clipY2)
	if (clipY2 > y + getHeight() - 1) {
		clipY2 = y + getHeight() - 1;
	}
	
	if(8<pixelsPerRow && pixelsPerRow<=12){
		rowMax=12;
	}
	
	while (y <= clipY2) {
		u16 rowCount = 0;
		u16 rowX = x;
		curr = *pixelData++;
		while (rowCount < rowMax) {
			// if we have runout, get next chunk
			// unpack next pixel
			mask = 1 << rowCount++;
			if (curr & mask) {
				// if we need to, write it to the bitmap
				if ((rowX >= clipX1 && rowX <= clipX2) && (y >= clipY1 && y <= clipY2)) 	// not clipped X-wise
				{
					bitmap->setPixel(rowX, y, colour);
				}
			}
			rowX++;
			if(pixelsPerRow<=8 && rowCount==8) {
				y++;
				rowX = x;
			}
		}
		y++;
	}
}
